﻿using RockBus.Logging.Interfaces;
using RockBus.Monitoring.DataContracts;
using RockBus.Monitoring.DataContracts.Configuration;
using RockBus.Monitoring.Interfaces;
using RockBus.Utilities;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;

namespace RockBus.Monitoring
{
    public class CollectorManager : ICollectorManager
    {
        public CollectorManager(ILogger logger,
            ICollectorManagerConfiguration configuration)
        {
            this.Logger = logger;
            this.Configuration = configuration;

            this.Collectors = new List<PerformanceCounterCollectorBase>();
            foreach (var pcc in this.Configuration.MonitoringConfiguration.PerformanceCounterConfigurations)
            {
                PerformanceCounterCollectorBase collector = null;
                if (pcc is PerformanceCounterCategoryConfiguration)
                {
                    collector = new PerformanceCounterCategoryCollector(pcc as PerformanceCounterCategoryConfiguration, this.Configuration.MonitoringConfiguration.BufferSize);
                }
                else if (pcc is PerformanceCounterConfiguration)
                {
                    collector = new PerformanceCounterCollector(pcc as PerformanceCounterConfiguration, this.Configuration.MonitoringConfiguration.BufferSize);
                }
                else if (pcc is PerformanceCounterInstanceConfiguration)
                {
                    collector = new PerformanceCounterInstanceCollector(pcc as PerformanceCounterInstanceConfiguration, this.Configuration.MonitoringConfiguration.BufferSize);
                }
                this.Collectors.Add(collector);
            }
        }

        private ILogger Logger { get; set; }

        private ICollectorManagerConfiguration Configuration { get; set; }

        private List<PerformanceCounterCollectorBase> Collectors { get; set; }

        /// <summary>
        /// The timestamp of the last performance counter collection.
        /// </summary>
        public DateTime CollectionTimestamp { get; set; }

        /// <summary>
        /// The timestamp of the last performance counter snapshot.
        /// </summary>
        public DateTime SnapshotTimestamp { get; set; }

        private Timer CollectionTimer { get; set; }

        private void CollectionTimerCallback(object state)
        {
            this.Collect(DateTime.Now);
        }

        public IEnumerable<PerformanceCounterInstance> GetSnapshot()
        {
            if (null == this.Collectors)
            {
                return null;
            }

            var snapshot = new List<PerformanceCounterInstance>();
            foreach (var collector in this.Collectors)
            {
                snapshot.AddRange(collector.GetSnapshot());
            }

            return snapshot;
        }

        //private int _guard;

        private void Collect(DateTime timestamp)
        {
            if (this.Collectors.IsNullOrEmpty())
            {
                return;
            }

            if (timestamp < this.CollectionTimestamp)
            {
                return;
            }

            // Ensure this method runs only once at a time
            //if (Interlocked.CompareExchange(ref this._guard, 1, 0) == 1)
            //{
            //    this.Logger.LogWarning(MethodBase.GetCurrentMethod(), "Another method was already running, skipping this time.");
            //    return;
            //}

            foreach (var collector in this.Collectors)
            {
                collector.Collect();
            }

            this.CollectionTimestamp = timestamp;
            //_guard = 0;
        }

        private readonly object _lockObject = new object();

        public void Start()
        {
            if (this.CollectionTimer == null)
            {
                lock (_lockObject)
                {
                    if (this.CollectionTimer == null)
                    {
                        this.Logger.LogInfo(MethodBase.GetCurrentMethod(), "Starting CollectorManager CollectionTimer with interval configuration {0}", this.Configuration.MonitoringConfiguration.ToString());
                        this.CollectionTimer = new Timer(this.CollectionTimerCallback, null, this.Configuration.MonitoringConfiguration.CollectionIntervalMs, this.Configuration.MonitoringConfiguration.CollectionIntervalMs);
                    }
                }
            }
        }

        public void Stop()
        {
            if (this.CollectionTimer == null)
            {
                lock (_lockObject)
                {
                    if (this.CollectionTimer == null)
                    {
                        this.Logger.LogInfo(MethodBase.GetCurrentMethod(), "Stopping CollectorManager CollectionTimer with interval configuration {0}", this.Configuration.MonitoringConfiguration.ToString());
                        this.CollectionTimer.Dispose();
                        this.CollectionTimer = null;
                    }
                }
            }
        }
    }
}
